﻿using System;
using System.Windows.Media;
using Silverlight.Media.Encoder.Sinks;

namespace Silverlight.Media.Encoder
{
    public sealed class AudioVideoCaptureHelper
    {
        #region Local Fields
        private CaptureSource m_captureSource;
        private VideoCaptureDevice m_videoDevice;
        private AudioCaptureDevice m_audioDevice;

        private AudioSampleSink m_audioSampleSink;
        private VideoSampleSink m_videoSampleSink;
        private VideoBrush m_brush;
        #endregion

        public event EventHandler<VideoFormatChangedArgs> VideoFormatChanged;
        public event EventHandler<AudioFormatChangedArgs> AudioFormatChanged;
        public event EventHandler<OnSampleArgs> OnMediaSample;

        #region Event Invokers
        private void InvokeOnMediaSample(OnSampleArgs e)
        {
            EventHandler<OnSampleArgs> handler = OnMediaSample;
            if (handler != null) handler(this, e);
        }

        private void InvokeAudioFormatChanged(AudioFormatChangedArgs e)
        {
            var handler = AudioFormatChanged;
            if (handler != null) handler(this, e);
        }

        private void InvokeVideoFormatChanged(VideoFormatChangedArgs e)
        {
            var handler = VideoFormatChanged;
            if (handler != null) handler(this, e);
        }
        #endregion

        public AudioVideoCaptureHelper()
        {
            m_captureSource = new CaptureSource();

            m_audioSampleSink = new AudioSampleSink();
            m_audioSampleSink.AudioFormatChanged += AudioFormatChangedCallback;
            m_audioSampleSink.OnMediaSample += OnMediaSampleCallback;
            m_audioSampleSink.CaptureSource = m_captureSource;
            
            m_videoSampleSink = new VideoSampleSink();
            m_videoSampleSink.VideoFormatChanged += VideoFormatChangedCallback;
            m_videoSampleSink.OnMediaSample += OnMediaSampleCallback;
            m_videoSampleSink.CaptureSource = m_captureSource;

            m_videoDevice = m_captureSource.VideoCaptureDevice;
            m_audioDevice = m_captureSource.AudioCaptureDevice;

            m_brush = new VideoBrush();
            m_brush.SetSource(m_captureSource);
        }

        public bool HasVideoSource
        {
            get
            {
                return m_captureSource.VideoCaptureDevice != null;
            }
        }
        
        public VideoBrush Brush
        {
            get
            {
                return m_brush;
            }
        }

        public bool HasAudioSource
        {
            get
            {
                return m_captureSource.AudioCaptureDevice != null;
            }
        }

        private void OnMediaSampleCallback(object sender, OnSampleArgs e)
        {
            InvokeOnMediaSample(e);
        }

        private void VideoFormatChangedCallback(object sender, VideoFormatChangedArgs e)
        {
            InvokeVideoFormatChanged(e);
        }

        private void AudioFormatChangedCallback(object sender, AudioFormatChangedArgs e)
        {
            InvokeAudioFormatChanged(e);
        }

        public VideoCaptureDevice VideoDevice
        {
            get { return m_videoDevice; }
            set
            {
                m_videoDevice = value;
                m_captureSource.VideoCaptureDevice = m_videoDevice;
            }
        }

        public AudioCaptureDevice AudioDevice
        {
            get { return m_audioDevice; }
            set
            {
                m_audioDevice = value;
                m_captureSource.AudioCaptureDevice = m_audioDevice;
            }
        }

        public void PrepareCapture()
        {
            if (!CaptureDeviceConfiguration.RequestDeviceAccess() && !CaptureDeviceConfiguration.AllowedDeviceAccess)
                throw new InvalidOperationException("Could not aquire access to capture devices");

            m_videoSampleSink.TimeFactor = m_captureSource.VideoCaptureDevice.FriendlyName.ToUpper().Contains("ISIGHT") ? 2 : 1;
        }

        public void StartCapture()
        {
            PrepareCapture();

            m_captureSource.Start();
        }

        public void StopCapture()
        {
            m_captureSource.Stop();
        }
    }
}
